/*
 *
 *
 *
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hiredis/hiredis.h"
#include "redisconn.h"

#define REDISCONN_DBG(fmt, args...) printf("<%s:%d> "fmt, __FILE__, __LINE__, ##args)

redis_conn_ctx *redisconn_connect(char *ip, unsigned short port)
{
    redis_conn_ctx *ctx = NULL;
    redisContext *c = NULL;

    ctx = malloc(sizeof(redis_conn_ctx));
    if (!ctx) {
        printf("malloc redis context failed.\n");
        return NULL;
    }

    c = redisConnect(ip, port);
    if (!c || c->err) {
        printf("Connect to redisServer faile:%s\n",c->errstr);
        goto err;
    }
    REDISCONN_DBG("Connect to redisServer Success\n");

    ctx->c = c;
    return ctx;
err:
    if (ctx) {
        free(ctx);
        ctx = NULL;
    }
    if (c) {
        redisFree(c);
        c = NULL;
    }
    return NULL;
}



void redisconn_close(redis_conn_ctx *ctx)
{
    if (!ctx) {
        return;
    }

    if (ctx->c) {
        redisFree(ctx->c);
        ctx->c = NULL;
    }

    free(ctx);
    return;
}

void __redisconn_free_result(redis_conn_result *result)
{
    int i = 0;

    if (!result)
        return;

    if (result->type == REDIS_REPLY_ARRAY) {
        for (i = 0; i < result->array_count; i++) {
            __redisconn_free_result(&result->r.array[i]);        
        }

        free(result->r.array);
        result->r.array = NULL;
    }

    if (result->type ==  REDIS_REPLY_STRING) {
        free(result->r.str);
    }
}

void redisconn_free_result(redis_conn_result *result)
{
    if (!result)
        return;

    __redisconn_free_result(result);
    free(result);
    return;
}

int _redisconn_parse_result(redis_conn_result *result, redisReply *r)
{
    int ret = 0;
    int len = 0;
    int i = 0;

    result->type = r->type;

    switch(r->type){
    case REDIS_REPLY_STATUS:
        if (!(strcmp(r->str,"OK") == 0 || strcmp(r->str,"ok")==0)) {
            printf("redis failed cmd status:%s\n", r->str);
            goto failed;
        }
        result->r.status = REDIST_CONN_RESULT_ST_SUCCESS;

        break;

    case REDIS_REPLY_INTEGER:
        result->r.l = r->integer;  

        break;

    case REDIS_REPLY_STRING:
        //REDISCONN_DBG("redis reply string is :%s\n", r->str);
        if (!r->str || (len = strlen(r->str)) <= 0) {
            printf("redis str reply failed\n");
            goto failed;
        }
        result->r.str = malloc(len + 1);
        if (!result->r.str) {
            printf("redis malloc str failed\n");
            goto failed;
        }
        strcpy(result->r.str, r->str);

        break;

    case REDIS_REPLY_NIL:
        break;

    case REDIS_REPLY_ARRAY:
        if (r->elements == 0) {
            REDISCONN_DBG("redis array count is 0\n");
            break;
        }
        result->r.array = malloc(sizeof(redis_conn_result) * r->elements);
        if (!result->r.array) {
            printf("malloc r.array failed\n");
            goto failed;
        }
        result->array_count = r->elements;
        for (i = 0; i < r->elements; i++) {
            ret = _redisconn_parse_result(&result->r.array[i], r->element[i]);
            if (ret) {
                printf("parse redis result failed, index:%d, count:%d\n", i, r->elements);
                goto failed;
            }
        }
        
        break;
    };

    return 0;

failed:
    return -1;
}

redis_conn_result *redisconn_run_command(redis_conn_ctx *ctx, char *cmd, int cmd_len)
{
    redisReply* r = NULL;
    redis_conn_result *result = NULL;
    int ret = 0;
    
    if (!ctx || !cmd || cmd_len <= 0) {
        printf("redis run cmd with wrong parameters\n");
        return NULL;
    }

    r = (redisReply*)redisCommand(ctx->c, cmd);
    if (!r) {
        printf("run redis failed for cmd:%s\n", cmd);
        return NULL;
    }

    result = malloc(sizeof(redis_conn_result));
    if (!result) {
        printf("malloc result failed\n");
        goto failed;
    }
    memset(result, 0x0, sizeof(redis_conn_result));

    ret = _redisconn_parse_result(result, r);
    if (ret) {
        printf("parse redis result failed\n");
        goto  failed;
    }

    if (r) {
        freeReplyObject(r);
        r = NULL;
    }
    return result;

failed:
    if (r) {
        freeReplyObject(r);
        r = NULL;
    }
    redisconn_free_result(result);
    return NULL;
}

redis_conn_result *redisconn_get_subscribe(redis_conn_ctx *ctx)
{
    redisReply* r = NULL;
    redis_conn_result *result = NULL;
    int ret = 0;
    
    if (!ctx) {
        printf("redis get subscribe with wrong parameters\n");
        return NULL;
    }

    ret = redisGetReply(ctx->c, (void **)&r);
    if (ret != REDIS_OK) {
        printf("get redis subscribe reply failed\n");
        return NULL;
    }

    result = malloc(sizeof(redis_conn_result));
    if (!result) {
        printf("malloc result failed\n");
        goto failed;
    }
    memset(result, 0x0, sizeof(redis_conn_result));

    ret = _redisconn_parse_result(result, r);
    if (ret) {
        printf("parse redis result failed\n");
        goto  failed;
    }

    if (r) {
        freeReplyObject(r);
        r = NULL;
    }
    return result;

failed:
    if (r) {
        freeReplyObject(r);
        r = NULL;
    }
    redisconn_free_result(result);
    return NULL;
}




void __redisconn_print_result(redis_conn_result *result, int level)
{
    int k = level;
    int i = 0;

    if (!result) {
        return;
    }

    switch(result->type){
    case REDIS_REPLY_STATUS:
        while(level-- > 0) printf("\t");
        printf("status:%d\n", result->r.status);
        break;

    case REDIS_REPLY_INTEGER:
        while(level-- > 0) printf("\t");
        printf("integer:%d\n", result->r.l);
        break;

    case REDIS_REPLY_STRING:
        while(level-- > 0) printf("\t");
        printf("string:%s\n", result->r.str);
        break;

    case REDIS_REPLY_NIL:
        break;

    case REDIS_REPLY_ARRAY:
        for (i = 0; i < result->array_count; i++) {
            __redisconn_print_result(&result->r.array[i], level+1);
        }
        
        break;
    };

    return;
}

void redisconn_print_result(redis_conn_result *result)
{
    int i = 0;
    __redisconn_print_result(result, i);
}




